package doublelinkedlist;

import java.util.NoSuchElementException;

import iterator.Iterator;
import visitor.Visitor;
import wrapper.AbstractObject;
import wrapper.Comparable;
import container.Container;
import container.ContainerEmptyException;
import enumeration.Enumeration;

public class DoubleLinkedList extends AbstractObject implements Container {
	private Element head;
	private Element tail;

	public void prepend(Object item) {
		Element tmp = new Element(item, null, head);
		if (head == null)
			tail = tmp;
		head = tmp;
	}

	public void append(Object item) {
		Element tmp = new Element(item, tail, null);
		if (head == null)
			head = tmp;
		else
			tail.next = tmp;
		tail = tmp;
	}

	public void assign(DoubleLinkedList list) {
		if (list != this) {
			purge();
			for (Element ptr = list.head; ptr != null; ptr = ptr.next) {
				append(ptr.datum);
			}
		}
	}

	public void extract(Object item) {
		Element ptr = head;
		while (ptr != null && ptr.datum != item) {
			ptr = ptr.next;
		}
		if (ptr == null)
			throw new IllegalArgumentException("Item não encontrado.");
		if (ptr == head)
			head = ptr.next;
		else
			ptr.prev.next = ptr.next;
		if (ptr == tail)
			tail = ptr.prev;
	}

	public Element getHead() {
		return head;
	}

	public Element getTail() {
		return tail;
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return 0;
	}

	public boolean isEmpty() {
		return head == null;
	}

	public Object getFirst() {
		if (head == null)
			try {
				throw new ContainerEmptyException("A pilha está vazia");
			} catch (ContainerEmptyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		return head.datum;
	}

	public Object getLast() {
		try {
			if (tail == null)
				throw new ContainerEmptyException("A pilha está vazia");
		} catch (ContainerEmptyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return tail.datum;
	}

	@Override
	public boolean isFull() {
		// TODO Auto-generated method stub
		return false;
	}

	public void purge() {
		head = null;
		tail = null;
	}

	@Override
	public void accept(Visitor visitor) {
		for (DoubleLinkedList.Element ptr = getHead(); ptr != null; ptr = ptr
				.getNext()) {
			visitor.visit(ptr.getDatum());
			if (visitor.isDone())
				return;
		}
	}

	@Override
	public Enumeration getEnumeration() {
		return new Enumeration() {
			protected DoubleLinkedList.Element position = getHead();

			public boolean hasMoreElements() {
				return position != null;
			}

			public Object nextElement() {
				if (position == null)
					throw new NoSuchElementException(
							"Elemento não encontrado na lista");
				Object result = position.getDatum();
				position = position.getNext();
				return result;
			}
		};
	}

	@Override
	public Iterator getIterator() {
		return new Iterator()
		{
			protected DoubleLinkedList.Element position;
			
			public void init()
			{
				position = getHead();
			}
			
			public boolean test()
			{
				return position != null;
			}
			
			public void inc()
			{
				position = position.getNext();
			}
			
			public Object current()
			{
				return position.getDatum();
			}
		};
	}

	@Override
	protected int compareTo(Comparable arg) {
		// TODO Auto-generated method stub
		return 0;
	}

	public final class Element {
		Object datum;
		Element prev;
		Element next;

		Element(Object datum, Element prev, Element next) {
			this.datum = datum;
			this.prev = prev;
			this.next = next;
		}

		public Object getDatum() {
			return datum;
		}

		public Element getPrev() {
			return prev;
		}

		public Element getNext() {
			return next;
		}

		public void insertAfter(Object item) {
			next.prev = new Element(item, this, next);
			next = next.prev;
			if (tail == this)
				tail = next;
		}

		public void insertBefore(Object item) {
			prev = new Element(item, prev, this);
			if (this == head)
				head = prev;
		}

	}

}
